package com.kaigejava.rulesengine.engine.core.utils;


import com.kaigejava.rulesengine.engine.core.kie.*;
import com.kaigejava.rulesengine.engine.core.typedefine.SystemConstant;
import com.kaigejava.rulesengine.engine.dto.CreRuleDto;
import com.kaigejava.rulesengine.engine.fact.RecyclingFact;
import org.drools.compiler.kie.builder.impl.InternalKieModule;
import org.drools.template.ObjectDataCompiler;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.ReleaseId;
import org.kie.api.builder.model.KieBaseModel;
import org.kie.api.builder.model.KieModuleModel;
import org.kie.api.builder.model.KieSessionModel;
import org.kie.api.command.Command;
import org.kie.api.conf.EqualityBehaviorOption;
import org.kie.api.conf.EventProcessingOption;
import org.kie.api.runtime.ExecutionResults;
import org.kie.api.runtime.StatelessKieSession;
import org.kie.api.runtime.conf.ClockTypeOption;
import org.kie.internal.command.CommandFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author kaigejava
 */
public class KieUtils {
	private static final String RULES_PATH = "rules/";
	public static final String SRC_MAIN_RESOURCES = "src/main/resources";
	public static final String RESPONSE = "response";
	private static Logger error = LoggerFactory.getLogger("STDERR");
	private static Logger logger = LoggerFactory.getLogger(KieUtils.class);

	/**
	 * compile rules file base on template
	 * @return
	 * @throws Exception
	 */
	public static String templatize(CreRuleDto creRule) throws Exception {
		Map<String, Object> data = new HashMap<String, Object>(20);
		ObjectDataCompiler objectDataCompiler = new ObjectDataCompiler();
		InputStream ruleFile = null;
		ruleFile = getRuleFile("cre-rule.drl");
		data.put("ruleId",creRule.getRuleId());
		data.put("name",creRule.getRuleName());
		data.put("leftNum",creRule.getLeftNum());
		data.put("rightNum",creRule.getRightNum());
		data.put("intervalType",creRule.getIntervalType());
		data.put("type",creRule.getType());

		return objectDataCompiler.compile(Arrays.asList(data), ruleFile);
	}

	/**
	 * @param filename:the
	 *            drl template file name
	 * @return InputStream
	 * 
	 */
	private static InputStream getRuleFile(String filename) throws Exception {
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        Resource resource = resourcePatternResolver.getResource(RULES_PATH + filename);
        return resource.getInputStream();
	}

	/**
	 * 
	 * @des get all rules content
	 * @return
	 * @throws IOException
	 */
	private static List<CompiledRule> getRules() {
		List<CompiledRule> ls = new ArrayList<CompiledRule>();
		for (CopyOnWriteArrayList<CompiledRule> rules : RulesMapper.getRules().values()) {
			for (CompiledRule rule : rules) {
				ls.add(rule);
			}

		}
		return ls;
	}
	
	/**
	 * @des 初始化一个kjar：把原有的drl包含进新建的kjar中
	 * @param ks
	 * @param releaseId
	 * @param isStateful :true stateful, false stateless session
	 * @return
	 * @throws Exception
	 */
	public static InternalKieModule initKieJar(KieServices ks, ReleaseId releaseId, boolean isStateful) throws Exception {
		KieFileSystem kfs = createKieFileSystemWithPackage(ks, isStateful);
		kfs.writePomXML(getPom(releaseId));
		// get rules from IotRulesMapper
		for (CompiledRule rule : getRules()) {
			String ruleStr = addPkgName(rule.getContent());
			kfs.write(SRC_MAIN_RESOURCES + SystemConstant.RULE_PKG_PATH+"/rule_" + rule.getRuleId()+ ".drl", ruleStr);
		}

		KieBuilder kieBuilder = ks.newKieBuilder(kfs);
		if (!kieBuilder.buildAll().getResults().getMessages().isEmpty()) {
			error.error("KieBuilder build error {}.",kieBuilder.getResults().getMessages());
			throw new IllegalStateException("Error creating KieBuilder.");
		}
		return (InternalKieModule) kieBuilder.getKieModule();
	}

	public static InternalKieModule createKieJar(KieServices ks, ReleaseId releaseId, ResourceWrapper resourceWrapper) {
		KieFileSystem kfs = createKieFileSystemWithProject(ks, false);
		kfs.writePomXML(getPom(releaseId));
		kfs.write("src/main/resources/" + resourceWrapper.getTargetResourceName(), resourceWrapper.getResource());
		KieBuilder kieBuilder = ks.newKieBuilder(kfs);
		if (!kieBuilder.getResults().getMessages().isEmpty()) {
			error.error("error create KieBuilder,message {}.",kieBuilder.getResults().getMessages());
			throw new IllegalStateException("Error creating KieBuilder.");
		}
		return (InternalKieModule) kieBuilder.getKieModule();
	}

	/**
	 * @des create kie file system
	 * @param ks
	 * @param :sateful
	 *            ,false sateless session
	 * @return
	 */
	private static KieFileSystem createKieFileSystemWithProject(KieServices ks, boolean isStateful) {
		KieModuleModel kproj = ks.newKieModuleModel();
		KieBaseModel kieBaseModel1 = kproj.newKieBaseModel("KBase").setDefault(true)
				.setEqualsBehavior(EqualityBehaviorOption.EQUALITY)
				.setEventProcessingMode(EventProcessingOption.STREAM);
		// Configure the KieSession.
		kieBaseModel1.newKieSessionModel("KSession").setDefault(true)
				.setType(
						isStateful ? KieSessionModel.KieSessionType.STATEFUL : KieSessionModel.KieSessionType.STATELESS)
				.setClockType(ClockTypeOption.get("realtime"));
		
		KieFileSystem kfs = ks.newKieFileSystem();
		kfs.writeKModuleXML(kproj.toXML());
		return kfs;
	}

	/**
	 * @des generate a pom file
	 * @param releaseId
	 * @param dependencies
	 * @return
	 */
	public static String getPom(ReleaseId releaseId, ReleaseId... dependencies) {
		String pom = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
				+ "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
				+ "         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n"
				+ "  <modelVersion>4.0.0</modelVersion>\n" + "\n" + "  <groupId>" + releaseId.getGroupId()
				+ "</groupId>\n" + "  <artifactId>" + releaseId.getArtifactId() + "</artifactId>\n" + "  <version>"
				+ releaseId.getVersion() + "</version>\n" + "\n";
		if (dependencies != null && dependencies.length > 0) {
			pom += "<dependencies>\n";
			for (ReleaseId dep : dependencies) {
				pom += "<dependency>\n";
				pom += "  <groupId>" + dep.getGroupId() + "</groupId>\n";
				pom += "  <artifactId>" + dep.getArtifactId() + "</artifactId>\n";
				pom += "  <version>" + dep.getVersion() + "</version>\n";
				pom += "</dependency>\n";
			}
			pom += "</dependencies>\n";
		}
		pom += "</project>";
		return pom;
	}

	/**
	 *
	 * @param recyclingFact
	 * @return 0:ok, -1:system error
	 * @throws Exception
	 */
	public static KieResponse fireRules(RecyclingFact recyclingFact)
			throws Exception {
        StatelessKieSession session = KieContex.getKieContex().getJobSession(SystemConstant.K_BASE_NAME);
        if(session == null) {
			error.error("Can not get kie session by ruleType {},rules cannot be executed.", recyclingFact.getRuleType());
			KieResponse kieResponse = new KieResponse();
			kieResponse.setCode(503);
			return kieResponse;
		}
	    RuleGroupActivator activator = new RuleGroupActivator(recyclingFact.getRuleType() + "");
		RuleProxy proxy = new RuleProxy();
		return executeNoSync(activator,proxy, recyclingFact,session);

	}

	/**
	 *
	 * @param activator
	 * @param proxy
	 * @param recyclingFact
	 * @param session
	 * @return drools response
	 */
	private static KieResponse  executeNoSync(RuleGroupActivator activator, RuleProxy proxy,
                                              RecyclingFact recyclingFact, StatelessKieSession session) {
		List<Command> cmds = new ArrayList<Command>();
		KieResponse kieResponse = new KieResponse();
		cmds.add(CommandFactory.newInsert(activator, "activator"));
		cmds.add(CommandFactory.newInsert(proxy, "proxy"));
		cmds.add(CommandFactory.newInsert(recyclingFact, "recyclingData"));
		cmds.add(CommandFactory.newInsert(kieResponse, "kieResponse"));
		//global
		cmds.add(CommandFactory.newSetGlobal(RESPONSE,kieResponse,true));
		session.addEventListener(new TrackingAgendaEventListener());

		//fact handle debug
		ExecutionResults executionResults = session.execute(CommandFactory.newBatchExecution(cmds));
		KieResponse resultsValue = (KieResponse)executionResults.getValue(RESPONSE);
		return resultsValue;
	}

	/**
	 * @des 创建 META-INF/kmodule.xml 文件
	 * @param ks
	 * @param isStateful    :true sateful,false sateless session
	 * <kmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
				xmlns="http://www.drools.org/xsd/kmodule">
			<kbase name="kbase-credit" default="false" eventProcessingMode="stream"
	                           equalsBehavior="equality" declarativeAgenda="disabled"
	                           scope="javax.enterprise.context.ApplicationScoped"
	                           packages="rules.pkg.*">
				<ksession name="ksession-rules-credit" type="stateless" default="false"
	                           clockType="realtime" beliefSystem="simple"/>
			</kbase>
		</kmodule>
	 * @return
	 */
	public static KieFileSystem createKieFileSystemWithPackage(KieServices ks, boolean isStateful) {
		KieModuleModel kproj = ks.newKieModuleModel();
			KieBaseModel kBaseModel = kproj.newKieBaseModel(SystemConstant.K_BASE_NAME)
					.setEqualsBehavior(EqualityBehaviorOption.EQUALITY)
					.setEventProcessingMode(EventProcessingOption.STREAM);
			/**
			 * 规则文件路径：packages="rules.pkg.*"
			 */
			String pkgName = SystemConstant.RULE_PKG_PREFIX+".*";
			kBaseModel.addPackage(pkgName);
			//specify session for use of stateful session,now meaningless
			String kSessionName = "ksession-rules-credit";
			// Configure the KieSession.
			kBaseModel.newKieSessionModel(kSessionName).setDefault(false)
					.setType(isStateful ? KieSessionModel.KieSessionType.STATEFUL : KieSessionModel.KieSessionType.STATELESS)
					.setClockType(ClockTypeOption.get("realtime"));


		KieFileSystem kfs = ks.newKieFileSystem();
		kfs.writeKModuleXML(kproj.toXML());
		return kfs;
	}

	/**
	 * add package name to rules file
	 * @param str
	 * @return
	 */
	private static String addPkgName(String str) {
		String pkgName = "package " + SystemConstant.RULE_PKG_PREFIX  + ";\n\n";
		String drlStr = pkgName + str;
		return drlStr;
	}

}
